﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CodeTalk.CTSpeechCommands;
using System.Speech.Recognition;

namespace CodeTalk
{
    public static class ActionManager
    {
        private struct SpeechAction
        {
            public SpeechCommand Command;
            public SpeechMode CallingMode;
            public string RecognizedText;
            private SemanticValue semValue;
            private object undoObject;

            public SpeechAction(SpeechCommand command, SpeechMode callingMode, string recognizedText, SemanticValue semValue)
            {
                Command = command;
                CallingMode = callingMode;
                RecognizedText = recognizedText;
                this.semValue = semValue;
                undoObject = null;
            }

            public void Execute()
            {
                undoObject = Command.Execute(CallingMode, RecognizedText, semValue);
            }

            public void Undo()
            {
                Command.Undo(CallingMode, RecognizedText, semValue, undoObject);
            }
        }

        private static Stack<SpeechAction> undoStack;
        private static Stack<SpeechAction> redoStack;

        private static List<SpeechAction> alternatives;

        static ActionManager()
        {
            undoStack = new Stack<SpeechAction>();
            redoStack = new Stack<SpeechAction>();
            alternatives = new List<SpeechAction>();
        }

        private static void ExecuteCommand(SpeechAction action)
        {
            action.Execute();

            if (action.Command.IsUndoable)
            {
                undoStack.Push(action);
                if (redoStack.Count > 0)
                    redoStack.Clear();
            }
        }

        internal static void ExecuteCommand(SpeechCommand command, SpeechMode callingMode, string recognizedText, SemanticValue semValue)
        {
            ExecuteCommand(new SpeechAction(command, callingMode, recognizedText, semValue));
        }

        internal static string UndoCommand()
        {
            if (undoStack.Count == 0)
                return null;

            SpeechAction action = undoStack.Pop();
            action.Undo();
            redoStack.Push(action);
            return action.Command.Name;
        }

        internal static void UndoCommand(int nTimes)
        {
            for (int i = 0; i < nTimes; i++)
                UndoCommand();
        }

        internal static string RedoCommand()
        {
            if (redoStack.Count == 0)
                return null;

            SpeechAction action = redoStack.Pop();
            action.Execute();
            undoStack.Push(action);
            return action.Command.Name;
        }

        public static void ClearHistory()
        {
            undoStack.Clear();
            redoStack.Clear();
        }

        internal static void AddAlternativeAction(SpeechCommand command, SpeechMode callingMode, string recognizedText, SemanticValue semValue)
        {
            alternatives.Add(new SpeechAction(command, callingMode, recognizedText, semValue));
        }

        internal static void ClearAlternativeActions()
        {
            alternatives.Clear();
        }

        internal static bool Correction(int index)
        {
            if (index < 0 || index > alternatives.Count)
                return false;

            if (UndoCommand() == null)
                return false;

            ExecuteCommand(alternatives[index-1]);
            return true;
        }
    }
}
